# -*- coding: utf-8 -*-
# @Author: hunan
# @Date: 2024/12/5 17:15
# @Description:
import json
import logging
import time

import requests

from product_upload.domain.basic.basic_product import BasicProduct
from product_upload.util.basic.common_util import sleep_random_duration, filter_table_data
from product_upload.util.basic.mysql_util import db_list_by_page, db_batch_insert, db_batch_update, db_get_one

logger = logging.getLogger(__name__)
# allen
# 55909903_USA_release
# acae5ed276be4312ab31be53fd110cd0
# hukexin
# 93810033_USA_release
# fff377d908b74321874db49613aad14e
# waigou
# 52489612_USA_release
# dfbe276664c749169c307a7b6fd18413
# cybirthk
# 22099455_USA_release
# 1c99488bce444942bb29b11f7d996dbb

client_list = [['55909903_USA_release', 'acae5ed276be4312ab31be53fd110cd0'], ['93810033_USA_release', 'fff377d908b74321874db49613aad14e'], ['52489612_USA_release', 'dfbe276664c749169c307a7b6fd18413'], ['22099455_USA_release', '1c99488bce444942bb29b11f7d996dbb']]


def get_access_token(client_id, client_secret):
    url = "https://api.gigacloudlogistics.com/api-auth-v1/oauth/token"  # 替换为实际的 API 端点
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    payload = {'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret}
    response = requests.post(url, data=payload, headers=headers, timeout=15)
    if response.status_code == 200:
        token_data = response.json()
        access_token = token_data.get('access_token')
        return access_token
    else:
        logger.error(f"Failed to obtain access token. Status Code: {response.status_code}")
        logger.error(response.text)
        return None


def get_sku_list(access_token, limit=10000, page=1):
    url = "https://api.gigacloudlogistics.com/api-b2b-v1/product/skus"
    headers = {
        'Authorization': f'Bearer {access_token}',  # Use your authentication mechanism
        'Content-Type': 'application/json'
    }
    params = {
        'limit': limit,
        'page': page
    }
    response = requests.get(url, headers=headers, params=params, timeout=15)
    if response.status_code == 200:
        response_data = response.json()
        if response_data.get("success"):
            skus_ = response_data.get("data", [])
            logger.info("个数", len(skus_))
            sku_list_ = []
            if len(skus_) == 0:
                return sku_list_
            for sku_u in skus_:
                sku_list_.append(sku_u.get('sku'))
            return sku_list_
        else:
            logger.error("Request failed. Error:", response_data)
    else:
        logger.error(f"Failed to get SKU list. Status Code: {response.status_code}")
        logger.error(response.text)
    return None


def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False


def clean_and_filter_urls(urls):
    cleaned_urls = []
    for url in urls:
        base_url = url.split('?', 1)[0]
        if not base_url.lower().endswith('.gif'):
            cleaned_urls.append(base_url)
    return cleaned_urls


def get_numeric_value(product, key, default=1):
    value = product.get(key)
    if value is None or not isinstance(value, (int, float)):
        try:
            value = float(value)
        except (ValueError, TypeError):
            return default
    return value if isinstance(value, (int, float)) else default


# 仅仅去更新产品信息
def only_update_product_info(access_token):
    for page in range(1, 1000):
        db_data = db_list_by_page("basic_product", "id,sku", "platform='GG'", page, 200)
        if len(db_data) == 0:
            break
        logger.info(f"page:{page}")
        sku_id_map = {x[1]: x[0] for x in db_data}
        _gg_data = get_product_details(access_token, list(sku_id_map.keys()))
        sleep_random_duration(3, 5)
        if page % 20 == 0 and page > 1:
            sleep_random_duration(7, 10)
        update_list = []
        for sku, product in _gg_data.items():
            if sku_id_map.get(sku):
                update_list.append([sku_id_map.get(sku), product["origin"]])
        db_batch_update("basic_product", ["id", "product_info"], update_list)


def get_product_details(access_token, skus):
    url = "https://api.gigacloudlogistics.com/api-b2b-v1/product/detailInfo"

    headers = {
        'Authorization': f'Bearer {access_token}',  # Replace with your actual Bearer token if needed
        'Content-Type': 'application/json'
    }
    data = {
        "skus": skus
    }

    response = requests.post(url, headers=headers, json=data, timeout=15)
    res_data = {}
    if response.status_code == 200:
        response_data = response.json()
        if response_data.get("success"):
            data = response_data.get("data", [])
            for product in data:
                keys_to_remove = [k for k, v in product.items() if
                                  v is None or v == "" or (isinstance(v, list) and len(v) == 0)]
                for key in keys_to_remove:
                    del product[key]
                sku = product['sku']
                package_length = get_numeric_value(product, "length")
                package_width = get_numeric_value(product, "width")
                package_height = get_numeric_value(product, "height")
                package_weight = get_numeric_value(product, "weight")
                is_combo = product.get("comboFlag", False)
                if is_combo:
                    combo_info = product.get("comboInfo", [])
                    temp_product = combo_info[0]
                    package_length = get_numeric_value(temp_product, "length")
                    package_width = get_numeric_value(temp_product, "width")
                    package_height = get_numeric_value(temp_product, "height")
                    package_weight = get_numeric_value(temp_product, "weight")
                packages = [package_length, package_width, package_height, package_weight]
                assembled_length = get_numeric_value(product, "assembledLength")
                assembled_width = get_numeric_value(product, "assembledWidth")
                assembled_height = get_numeric_value(product, "assembledHeight")
                assembled_weight = get_numeric_value(product, "assembledWeight")
                dimensions = [assembled_length, assembled_width, assembled_height, assembled_weight]
                properties = f'{product.get("attributes", "")}\n{filter_table_data(product.get("description", ""))}'
                description = product.get("characteristics", "")
                title = product.get("name", "")
                not_available = product.get("unAvailablePlatform", [])
                published = product.get("toBePublished", True)
                image_urls = product.get("imageUrls", [])
                images_list = clean_and_filter_urls(image_urls)
                main_image = ""
                if len(images_list) > 0:
                    main_image = images_list[0]
                published = 1 if published else 0
                remove_extra(product)
                product["description"] = filter_table_data(product.get("description", ""))
                origin = dict_to_custom_string(product)
                origin = origin.replace("@&@", "\n").replace("\n\n\n", "\n").replace("\n\n", "\n")
                res_data[sku] = {"sku": sku, "packages": packages, "dimensions": dimensions, "properties": properties,
                                 "description": description, "title": title, "not_available": not_available,
                                 "published": published, "main_image": main_image, "images_list": images_list,
                                 "origin": origin}
            return res_data
        else:
            logger.error(f"Request failed. Error:{response_data}")
    else:
        # Error handling if the request fails
        logger.error(f"Failed to get product details. Status Code: {response.status_code}")
        logger.error(response.text)

    return None


def remove_extra(product):
    if product.get("imageUrls", None) is not None:
        del product["imageUrls"]
    if product.get("fileUrls", None) is not None:
        del product["fileUrls"]
    if product.get("toBePublished", None) is not None:
        del product["toBePublished"]
    if product.get("mpn", None) is not None:
        del product["mpn"]
    if product.get("gigaIndex", None) is not None:
        del product["gigaIndex"]
    if product.get("sellerType", None) is not None:
        del product["sellerType"]
    if product.get("sellerInfo", None) is not None:
        del product["sellerInfo"]
    if product.get("firstAvailableDate", None) is not None:
        del product["firstAvailableDate"]
    if product.get("sellerMessageResponseRate", None) is not None:
        del product["sellerMessageResponseRate"]
    if product.get("sellerReturnApprovalRate", None) is not None:
        del product["sellerReturnApprovalRate"]
    if product.get("sellerCode", None) is not None:
        del product["sellerCode"]
    if product.get("sellerReturnRate", None) is not None:
        del product["sellerReturnRate"]
    if product.get("associateProductList", None) is not None:
        del product["associateProductList"]
    if product.get("unAvailablePlatform", None) is not None:
        del product["unAvailablePlatform"]
    if product.get("categoryCode", None) is not None:
        del product["categoryCode"]
    if product.get("category", None) is not None:
        del product["category"]
    if product.get("comboFlag", None) is not None:
        del product["comboFlag"]
    if product.get("certificationList", None) is not None:
        del product["certificationList"]
    if product.get("videoUrls", None) is not None:
        del product["videoUrls"]
    if product.get("upc", None) is not None:
        del product["upc"]
    if product.get("overSizeFlag", None) is not None:
        del product["overSizeFlag"]
    if product.get("partFlag", None) is not None:
        del product["partFlag"]
    if product.get("customized", None) is not None:
        del product["customized"]


def dict_to_custom_string(d):
    if isinstance(d, str):
        return d
    elif isinstance(d, dict):
        parts = []
        for key, value in d.items():
            if isinstance(value, list):
                value_str = ','.join(map(str, value))
            elif isinstance(value, dict):
                value_str = dict_to_custom_string(value)  # 递归处理字典
            else:
                value_str = str(value)
            parts.append(f"{key}:{value_str}")
        return '@&@'.join(parts)
    else:
        return str(d)


def get_product_price(access_token, skus):
    url = "https://api.gigacloudlogistics.com/api-b2b-v1/product/price"
    headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'}
    data = {"skus": skus}
    attempt = 0
    max_retries = 2
    while attempt < max_retries:
        try:
            response = requests.post(url, headers=headers, json=data, timeout=20)
            if response.status_code == 200:
                response_data = response.json()
                if response_data.get("success"):
                    res_data = {}
                    for product in response_data.get("data", []):
                        sku = product.get("sku", "")
                        shipping_fee = get_numeric_value(product, "shippingFee", 0)
                        shipping_fee_range = product.get("shippingFeeRange", None)
                        if shipping_fee_range:
                            shipping_fee = get_numeric_value(shipping_fee_range, "maxAmount", 0)
                        price = get_numeric_value(product, "exclusivePrice", 0)
                        if price == 0:
                            price = get_numeric_value(product, "discountedPrice", 0)
                        if price == 0:
                            price = get_numeric_value(product, "price", 0)
                        # 如果存在sku,那么判断一下,当前是否可售,可售则覆盖
                        if res_data.get(sku):
                            if product.get("skuAvailable", False):
                                res_data[sku] = {"sku": sku, "price": price, "ship_fee": shipping_fee}
                        else:
                            res_data[sku] = {"sku": sku, "price": price, "ship_fee": shipping_fee}
                    return res_data
                else:
                    logger.error(f"Request failed. Error:{response_data}")
                    break  # 如果请求成功但业务逻辑失败，则不再重试
            else:
                logger.error(f"Failed to get product price. Status Code: {response.status_code}")
                logger.error(response.text)
                break  # 如果状态码不是200，通常不需要重试
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            if attempt + 1 == max_retries:
                logger.error(f"Request failed after {max_retries} attempts. Last error: {str(e)}")
                return None
            else:
                attempt += 1  # 准备下一次尝试
        except Exception as e:
            logger.error(f"Unexpected error occurred: {str(e)}")
            return None


def get_product_quantity(access_token, skus):
    url = "https://api.gigacloudlogistics.com/api-b2b-v1/product/quantity"
    headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'}
    data = {"skus": skus}
    attempt = 0
    max_retries = 2
    while attempt < max_retries:
        try:
            response = requests.post(url, headers=headers, json=data, timeout=20)
            if response.status_code == 200:
                response_data = response.json()
                if response_data.get("success"):
                    res_data = {}
                    for product in response_data.get("data", []):
                        sku = product.get("sku", "")
                        quantity = get_numeric_value(product, "quantity", 0)
                        res_data[sku] = {"sku": sku, "quantity": quantity}
                    return res_data
                else:
                    logger.error(f"quantity Request failed. Error:{response_data}")
                    break  # 如果请求成功但业务逻辑失败，则不再重试
            else:
                logger.error(f"Failed to get product quantity. Status Code: {response.status_code}")
                logger.error(response.text)
                break  # 如果状态码不是200，通常不需要重试
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            if attempt + 1 == max_retries:
                logger.error(f"Request failed after {max_retries} attempts. Last error: {str(e)}")
                return None
            else:
                attempt += 1  # 准备下一次尝试
        except Exception as e:
            logger.error(f"Unexpected error occurred: {str(e)}")
            return None


def merge_product(base_data, price_data, quantity_data):
    res_list = []
    for sku in list(base_data.keys()):
        row_dict = base_data[sku]
        row_dict.update(price_data[sku])
        row_dict.update(quantity_data[sku])
        origin = row_dict.get("origin", None)
        if origin is not None:
            del row_dict["origin"]
        else:
            origin = ""
        del row_dict["properties"]
        del row_dict["description"]
        json_text = json.dumps(row_dict)
        dimensions = f'{row_dict["dimensions"][0]} * {row_dict["dimensions"][1]} * {row_dict["dimensions"][2]} * {row_dict["dimensions"][3]}'
        packages = f'{row_dict["packages"][0]} * {row_dict["packages"][1]} * {row_dict["packages"][2]} * {row_dict["packages"][3]}'
        ordered_list = [
            "GG",
            row_dict.get('sku', ""),
            row_dict.get('main_image', ""),
            row_dict.get('title', ""),
            dimensions,
            packages,
            row_dict.get('price', 0),
            int(row_dict.get('quantity', 0)),
            json.dumps(row_dict.get('not_available', [])),
            row_dict.get('published', 0),
            json_text, origin]
        res_list.append(ordered_list)
    return res_list


# 新增GG平台商品
def insert_gg_product():
    _sku_list = []
    for cli_idx in range(len(client_list)):
        token_ = get_access_token(client_list[cli_idx][0], client_list[cli_idx][1])
        for page in range(30):
            _sku_list = get_sku_list(token_, 10000, page + 1)
            sleep_random_duration(5, 8)
            if len(_sku_list) == 0:
                break
            _sku_str = [f"'{x}'" for x in _sku_list]
            db_skus = db_list_by_page("basic_product", "sku", f'platform="GG" and sku in ({",".join(_sku_str)})', None, 1, 10000)
            if db_skus is not None:
                db_sku_list = [x[0] for x in db_skus]
                _sku_list = list(set(_sku_list) - set(db_sku_list))
            if len(_sku_list) == 0:
                continue
            for idx in range(0, len(_sku_list), 200):
                _skus = _sku_list[idx:idx + 200]
                base_data_ = get_product_details(token_, _skus)
                price_data = get_product_price(token_, _skus)
                quantity_data = get_product_quantity(token_, _skus)
                sleep_random_duration(4, 7)
                result_list = merge_product(base_data_, price_data, quantity_data)
                need_insert = []
                # need_update = []
                sku_list = []
                for item in result_list:
                    sku = item[1]
                    sku_list.append(sku)
                    prod = db_get_one("basic_product", f'sku = "{sku}" and platform ="GG"', BasicProduct)
                    if prod is None:  # 新增
                        need_insert.append(item)
                if len(need_insert) > 0:
                    print("新增产品:", len(need_insert))
                    db_batch_insert("basic_product",
                                    ["platform", "sku", "main_image", "title", "dimensions", "packages", "price",
                                     "quantity",
                                     "not_available", "published", "json_text", "product_info"], need_insert)


# 更新库存
def api_update_gg_quantity(token, db_skus, sku_id_map, db_quantity_map):
    quantity_data = get_product_quantity(token, db_skus)
    need_update = []
    if quantity_data:
        for val in list(quantity_data.values()):
            sku = val.get('sku', "")
            api_quantity = int(val.get('quantity', 0))
            id_ = sku_id_map[sku]
            if id_ is None:
                continue
            db_quantity_ = db_quantity_map.get(sku, None)
            db_quantity = int(db_quantity_) if db_quantity_ else 0
            if db_quantity != api_quantity:
                need_update.append([id_, api_quantity])
    if need_update:
        db_batch_update("basic_product", ["id", "quantity"], need_update)


def api_update_gg_price(token, db_skus, sku_id_map, sku_price_map, sku_ship_fee_map):
    price_data = get_product_price(token, db_skus)
    need_update = []
    image_update = []
    if price_data:
        for val in list(price_data.values()):
            sku = val.get('sku', "")
            api_price = round(float(val.get('price', 0)), 2)
            api_ship_fee = round(float(val.get('ship_fee', 0)), 2)
            id_ = sku_id_map.get(sku, None)
            if id_ is None:
                continue
            db_price = round(float(sku_price_map.get(sku, 0)), 2) if sku_price_map.get(sku, 0) else 0
            db_ship_fee = round(float(sku_ship_fee_map.get(sku, 0)), 2) if sku_ship_fee_map.get(sku, 0) else 0
            published = 1 if api_price else 0
            if db_price != api_price or db_ship_fee != api_ship_fee:
                image_update.append([id_, published])
                need_update.append([id_, published, api_price, api_ship_fee])
    if len(image_update) > 0:
        db_batch_update("basic_image", ["basic_id", "published"], image_update)
        time.sleep(1)
        db_batch_update("basic_product", ["id", "published", "price", "ship_fee"], need_update)


def flush_api_gg_quantity():
    db_gg_sku_list = db_list_by_page("basic_product", "id,sku,quantity", f'platform="GG"', BasicProduct, 1, 99999999)
    sku_id_map_ = {tmp_prod.sku: tmp_prod.id for tmp_prod in db_gg_sku_list}
    sku_quantity_map_ = {tmp_prod.sku: tmp_prod.quantity for tmp_prod in db_gg_sku_list}
    db_all_skus = [tmp_prod.sku for tmp_prod in db_gg_sku_list]
    logger.info(f'start: giga api quantity,total:{len(db_all_skus)} sku,guess need {len(db_all_skus) // 8000} minutes')
    tokens_ = []
    for current_client in client_list[1:]:
        tokens_.append(get_access_token(current_client[0], current_client[1]))
    index = 0
    for idx in range(0, len(db_all_skus), 200):
        _skus = db_all_skus[idx:idx + 200]
        api_update_gg_quantity(tokens_[index % len(tokens_)], _skus, sku_id_map_, sku_quantity_map_)
        index += 1
        if index % (len(tokens_) * 4) == 0:
            time.sleep(10)
    logger.info('quantity giga api success end.')


def flush_api_gg_price():
    db_gg_sku_list = []
    for page in range(1, 99999):
        db_gg_this = db_list_by_page("basic_product", "id,sku,price", f'platform="GG"', BasicProduct, page, 50000)
        if not db_gg_this:
            break
        db_gg_sku_list.extend(db_gg_this)
    sku_id_map_ = {tmp_prod.sku: tmp_prod.id for tmp_prod in db_gg_sku_list}
    sku_price_map_ = {tmp_prod.sku: tmp_prod.price for tmp_prod in db_gg_sku_list}
    sku_ship_fee_map_ = {tmp_prod.sku: tmp_prod.ship_fee for tmp_prod in db_gg_sku_list}
    db_all_skus = [tmp_prod.sku for tmp_prod in db_gg_sku_list]
    logger.info(f'start: giga api price,total:{len(db_all_skus)} sku,guess need {len(db_all_skus) // 3300} minutes')
    index = 0
    _token = get_access_token(client_list[0][0], client_list[0][1])
    for idx in range(0, len(db_all_skus), 200):
        _skus = db_all_skus[idx:idx + 200]
        api_update_gg_price(_token, _skus, sku_id_map_, sku_price_map_, sku_ship_fee_map_)
        index += 1
        if index % 5 == 0:
            time.sleep(10)
    logger.info('end: price giga api success.')


if __name__ == '__main__':
    # flush_api_gg_price()
    token = get_access_token(client_list[0][0], client_list[0][1])
    quantity_data = get_product_price(token, ["W21949176", "B062P290285", "W210S00429"])
    print(quantity_data)
    pass
